﻿////////////////////////////////////////////////////////////////////////////
//
// Immense Engine Source File.
//  Copyright (C), Indloon 2012
// -------------------------------------------------------------------------
//  File name:   ieMathVector4.h
//  Description: ieMath vector 4D
//  Created:     8/12/2012 Genert Org
//  Compilers:   Visual C++ 2010 Express
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef __IMMENSEENGINE_MATH_VECTOR4D_H
#define __IMMENSEENGINE_MATH_VECTOR4D_H
#pragma once

/*  1. C L A S S
	=========================================================================== */

class ieVec4 {
public:
    float x;
	float y;
	float z;
	float w;
                    ieVec4( void ); // type init
    explicit        ieVec4( const float value );
    explicit        ieVec4( const float x, const float y, const float z, const float w );    

    float			operator [] ( const int index ) const;
    float &			operator [] ( const int index );
	ieVec4 &        operator = ( const ieVec4 &vec );
    bool            operator == ( const ieVec4 &vec ) const;
    bool            operator != ( const ieVec4 &vec ) const;
    ieVec4          operator - () const;
    ieVec4          operator + ( const ieVec4 &vec ) const;
    ieVec4          operator - ( const ieVec4 &vec ) const;
    ieVec4          operator * ( const ieVec4 &vec ) const;
    ieVec4          operator / ( const ieVec4 &vec ) const;
    ieVec4          operator + ( const float value ) const;
    ieVec4          operator - ( const float value ) const;
    ieVec4          operator * ( const float value ) const;
    ieVec4          operator / ( const float value ) const;
    ieVec4 &	    operator += ( const ieVec4 &vec );
    ieVec4 &		operator -= ( const ieVec4 &vec );
    ieVec4 &		operator *= ( const ieVec4 &vec );
    ieVec4 &		operator /= ( const ieVec4 &vec );
    ieVec4 &		operator += ( const float value );
    ieVec4 &		operator -= ( const float value );
    ieVec4 &		operator *= ( const float value );
    ieVec4 &		operator /= ( const float value );
	bool            operator < ( const ieVec4 &vec ) const;
	bool            operator > ( const ieVec4 &vec ) const;
	
	void            Set( const float value );
	void            Set( const float x, const float y, const float z, const float w );
	void            Clear( void );
	void            Zero( void );
	bool            IsCleared( void );
	bool            IsZero( void );
	
	bool            AreEqual( const ieVec4 &vec ) const;
	bool            AreEqual( const ieVec4 &vec, float epsilon ) const;
	float           Length( void ) const;
	float			LengthFast( void ) const;
	float			LengthSqr( void ) const;
	float           Normalize( void );
	void            NormalizeFast( void );
	void			Truncate( void );
	void			Clamp( const ieVec4 &min, const ieVec4 &max );
	void			Clamp( const float min, const float max );
	void			Average(const ieVec4 &vec1, const ieVec4 &ieVec2 );
	void            Snap( void );
	void			Lerp( const ieVec4 &from, const ieVec4 &to, const float frac );
	void			LerpFast( const ieVec4 &from, const ieVec4 &to, const float frac );
	float			Distance( const ieVec4 &vec ) const;
	float			DistanceFast( const ieVec4 &vec ) const;
	float			DistanceSqr( const ieVec4 &vec ) const;

	ieVec2 &        ToVec2( void );
	ieVec3 &        ToVec3( void );
};


/*  2. F U N C T I O N S
	=========================================================================== */

// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4::ieVec4( void )
{
    // ...
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4::ieVec4( const float value )
{
    this->x = value;
	this->y = value;
	this->z = value;
	this->w = value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4::ieVec4( const float x, const float y, const float z, const float w )
{
    this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::operator [] ( const int index ) const
{
    return ( &x )[index];
}
// -------------------------------------------------------------------------------------------------------------
ILINE float &ieVec4::operator [] ( const int index )
{
	return ( &x )[index];
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 &ieVec4::operator = ( const ieVec4 &vec ) 
{
    x = vec.x;
	y = vec.y;
	z = vec.z;
	w = vec.w;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::operator == ( const ieVec4 &vec ) const 
{
	if( ( x != vec.x ) || ( y != vec.y ) || ( z != vec.z ) || ( w != vec.w ) )
	{
        return false;
	} 
	else 
	{
        return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::operator != ( const ieVec4 &vec ) const 
{
	if( ( x == vec.x ) || ( y == vec.y ) || ( z ==vec.z ) || ( w == vec.w ) )
	{
        return false;
	} 
	else 
	{
        return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator - () const 
{
	return ieVec4( -x, -y, -z, -w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator + ( const ieVec4 &vec ) const 
{
	return ieVec4( x + vec.x, y + vec.y, z + vec.z, w + vec.w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator - ( const ieVec4 &vec ) const 
{
	return ieVec4( x - vec.x, y - vec.y, z - vec.z, w - vec.w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator * ( const ieVec4 &vec ) const 
{
	return ieVec4( x * vec.x, y * vec.y, z * vec.z, w * vec.w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator / ( const ieVec4 &vec ) const 
{
	return ieVec4( x / vec.x, y / vec.y, z / vec.z, w / vec.w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator + ( const float value ) const 
{
	return ieVec4( x + value, y + value, z + value, w + value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator - ( const float value ) const 
{
	return ieVec4( x - value, y - value, z - value, w - value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator * ( const float value ) const 
{
	return ieVec4( x * value, y * value, z * value, w * value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 ieVec4::operator / ( const float value ) const 
{
	float	scale = 1.0f / value;

	return ieVec4( x * scale, y * scale, z * scale, w * scale );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 &ieVec4::operator += ( const float value )
{
	x += value;
	y += value;
	z += value;
    w += value;
	
	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 &ieVec4::operator -= ( const float value )
{
	x -= value;
	y -= value;
	z -= value;
    w -= value;
	
	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 &ieVec4::operator *= ( const float value )
{
	x *= value;
	y *= value;
	z *= value;
	w *= value;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec4 &ieVec4::operator /= ( const float value )
{
	float	scale = 1.0f / value;

	x *= scale;
	y *= scale;
	z *= scale;
	w *= scale;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::operator < ( const ieVec4 &vec ) const
{
    if( ( ( x < vec.x ) && ( y < vec.y ) && ( z < vec.z ) && ( w < vec.w ) ) || ( ( x < vec.x ) || ( y < vec.y ) || ( z < vec.z ) || ( w < vec.w ) ) )
	{
	    return true;
	}
	else if( ( ( x > vec.x ) && ( y > vec.y ) && ( z > vec.z ) && ( w > vec.w ) ) || ( ( x > vec.x ) || ( y > vec.y ) || ( z > vec.z ) || ( w > vec.w ) ) )
	{
	    return false;
	} 
	else
	{
	    return false;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::operator > ( const ieVec4 &vec ) const
{
    if( ( ( x > vec.x ) && ( y > vec.y ) && ( z > vec.z ) && ( w > vec.w ) ) || ( ( x > vec.x ) || ( y > vec.y ) || ( z > vec.z ) || ( w > vec.w ) ) )
	{
	    return true;
	}
	else if( ( ( x < vec.x ) && ( y < vec.y ) && ( z < vec.z ) && ( w < vec.w )) || ( ( x < vec.x ) || ( y < vec.y ) || ( z < vec.z ) || ( w < vec.w ) ) )
	{
	    return false;
	}
	else
	{
	    return false;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Set( const float value )
{
    this->x = value;
    this->y = value;
    this->z = value;
	this->w = value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Set( const float x, const float y, const float z, const float w )
{
    this->x = x;
    this->y = y;
    this->z = z;
	this->w = w;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Clear( void )
{
    x = y = z = w = 0.0f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Zero( void )
{
    x = y = z = w = 0.0f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::IsCleared( void )
{
    if( ( x != 0.0f ) ||
	    ( y != 0.0f ) ||
		( z != 0.0f ) ||
		( w != 0.0f ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::IsZero( void )
{
    if( ( x != 0.0f ) ||
	    ( y != 0.0f ) ||
		( z != 0.0f ) ||
		( w != 0.0f ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::AreEqual( const ieVec4 &vec ) const
{
    if( ( x != vec.x ) && ( y != vec.y ) && ( z != vec.z ) && ( w != vec.w ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec4::AreEqual( const ieVec4 &vec, float epsilon ) const
{
    return ( ( ieMath::Fabs( x - vec.x ) < epsilon ) && ( ieMath::Fabs( y - vec.y ) < epsilon ) && ( ieMath::Fabs( z - vec.z ) < epsilon ) && ( ieMath::Fabs( w - vec.w ) < epsilon ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::Length( void ) const
{
    float length;

	length = ( x * x ) + ( y * y ) + ( z * z ) + ( w * w );
	
	return ( (float) ieMath::Sqrt( length ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::LengthFast( void ) const 
{
	return ieMath::SqrtFast( ( x * x ) + ( y * y ) + ( z * z ) + ( w * w ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::LengthSqr( void ) const 
{
	return ( ( x * x ) + ( y * y ) + ( z * z ) + ( w * w ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::Normalize( void )
{
	float length, invLength;

	length = ieMath::Sqrt (( x * x ) + ( y * y ) + ( z * z ) + ( w * w ) );

	if( length )
	{
		invLength = 1.0f / length;

		x *= invLength;
		y *= invLength;
		z *= invLength;
		w *= invLength;
	}

	return length;
	
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::NormalizeFast( void )
{
	float	invLength;

	invLength = ieMath::RSqrt( ( x * x ) + ( y * y ) + ( z * z ) + ( w * w ) );

	x *= invLength;
	y *= invLength;
	z *= invLength;
	w *= invLength;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Truncate( void )
{
	x = ieMath::FloatToInt( x );
	y = ieMath::FloatToInt( y );
	z = ieMath::FloatToInt( z );
	w = ieMath::FloatToInt( w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Clamp( const ieVec4 &min, const ieVec4 &max )
{
	x = ( x < min.x ) ? min.x : ( x > max.x ) ? max.x : x;
	y = ( y < min.y ) ? min.y : ( y > max.y ) ? max.y : y;
	z = ( z < min.z ) ? min.z : ( z > max.z ) ? max.z : z;
	w = ( w < min.w ) ? min.w : ( w > max.w ) ? max.w : w;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Clamp( const float min, const float max )
{
	x = ( x < min ) ? min : ( x > max ) ? max : x;
	y = ( y < min ) ? min : ( y > max ) ? max : y;
	z = ( z < min ) ? min : ( z > max ) ? max : z;
	w = ( w < min ) ? min : ( w > max ) ? max : w;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Average( const ieVec4 &vec1, const ieVec4 &ieVec2 )
{
	x = ( vec1.x + ieVec2.x ) * 0.5f;
	y = ( vec1.y + ieVec2.y ) * 0.5f;
	z = ( vec1.z + ieVec2.z ) * 0.5f;
	w = ( vec1.w + ieVec2.w ) * 0.5f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Snap( void )
{
    x = ieMath::Round( x );
	y = ieMath::Round( y );
	z = ieMath::Round( z );
	w = ieMath::Round( w );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::Lerp( const ieVec4 &from, const ieVec4 &to, const float frac )
{
	if ( frac <= 0.0f )
	{
		*this = from;
		return;
	}

	if ( frac >= 1.0f )
	{
		*this = to;
		return;
	}

	x = from.x + ( to.x - from.x ) * frac;
	y = from.y + ( to.y - from.y ) * frac;
	z = from.z + ( to.z - from.z ) * frac;
	w = from.w + ( to.w - from.w ) * frac;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec4::LerpFast( const ieVec4 &from, const ieVec4 &to, const float frac )
{
	x = from.x + ( to.x - from.x ) * frac;
	y = from.y + ( to.y - from.y ) * frac;
	z = from.z + ( to.z - from.z ) * frac;
	w = from.w + ( to.w - from.w ) * frac;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::Distance( const ieVec4 &vec ) const 
{
	float	xd, yd, zd, wd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;
	wd = vec.w - w;

	return ieMath::Sqrt( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) + ( wd * wd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::DistanceFast( const ieVec4 &vec ) const 
{
	float	xd, yd, zd, wd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;
	wd = vec.w - w;

	return ieMath::SqrtFast( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) + ( wd * wd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec4::DistanceSqr( const ieVec4 &vec ) const
{
	float	xd, yd, zd, wd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;
	wd = vec.w - w;

	return ( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) + ( wd * wd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec2 &ieVec4::ToVec2( void )
{
	return ( *reinterpret_cast <ieVec2*>( this ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec4::ToVec3( void )
{
    return ( *reinterpret_cast <ieVec3*>( this ) );
}
// -------------------------------------------------------------------------------------------------------------


#endif // __IMMENSEENGINE_MATH_VECTOR4D_H